/**
 * 
 */
package pl.com.qapps.datastore.base;

import java.util.Collection;
import java.util.concurrent.ExecutorService;

import pl.com.qapps.datastore.Store;

import com.google.appengine.api.datastore.BaseDatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceConfig;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.TransactionOptions;
import com.google.apphosting.api.ApiProxy.ApiConfig;
import com.google.apphosting.api.DatastorePb;

/**
 * @author Jakub Dykowski
 * 
 */
abstract class BaseDatastoreServiceImpl implements BaseDatastoreService {

	protected final Store store;
	protected final TransactionStack txnProvider;
	protected final ExecutorService dispatcher;

	/*
	 * old
	 */
	private final QueryRunner queryRunner;
	protected final DatastoreServiceConfig datastoreServiceConfig;

	public BaseDatastoreServiceImpl(DatastoreServiceConfig cfg, Store store,
			TransactionStack stack, ExecutorService dispatcher) {
		this.dispatcher = dispatcher;
		this.datastoreServiceConfig = cfg;
		this.store = store;
		this.txnProvider = stack;
		// TODO fix it, so this can be used, looks useful
		this.queryRunner = new QueryRunner(store, dispatcher);
	}

	@Override
	public PreparedQuery prepare(Query query) {
		return prepare(null, query);
	}

	@Override
	public PreparedQuery prepare(Transaction txn, Query query) {
		ApiConfig apiConfig = new ApiConfig();
		MultiQueryBuilder queriesToRun = QuerySplitHelper.splitQuery(query);
		if (queriesToRun != null) {
			System.out.println("spliting into multiple queries");
			return new PreparedMultiQuery(apiConfig, datastoreServiceConfig,
					queriesToRun, txn, queryRunner);
		}
		System.out.println("running single query");
		return new PreparedQueryImpl(apiConfig, datastoreServiceConfig, query,
				txn, queryRunner);
	}

	@Override
	public Transaction getCurrentTransaction() {
		return txnProvider.peek();
	}

	@Override
	public Transaction getCurrentTransaction(Transaction returnedIfNoTxn) {
		return txnProvider.peek(returnedIfNoTxn);
	}

	@Override
	public Collection<Transaction> getActiveTransactions() {
		return txnProvider.getAll();
	}

	/**
	 * Helper class used to encapsulate the result of a call to
	 * {@link #getOrCreateTransaction()}.
	 */
	static final class GetOrCreateTransactionResult {

		private final boolean isNew;
		private final Transaction txn;

		GetOrCreateTransactionResult(boolean isNew, Transaction txn) {
			this.isNew = isNew;
			this.txn = txn;
		}

		/**
		 * @return {@code true} if the Transaction was created and should
		 *         therefore be closed before the end of the operation,
		 *         {@code false} otherwise.
		 */
		public boolean isNew() {
			return isNew;
		}

		/**
		 * @return The Transaction to use. Can be {@code null}.
		 */
		public Transaction getTransaction() {
			return txn;
		}
	}

	/**
	 * Return the current transaction if one already exists, otherwise create a
	 * new transaction or throw an exception according to the
	 * {@link ImplicitTransactionManagementPolicy}.
	 */
	GetOrCreateTransactionResult getOrCreateTransaction() {
		Transaction currentTxn = getCurrentTransaction(null);
		if (currentTxn != null) {
			return new GetOrCreateTransactionResult(false, currentTxn);
		}
		return new GetOrCreateTransactionResult(true, store
				.getTransactionCore().beginTransaction(txnProvider, dispatcher,
						TransactionOptions.Builder.withDefaults()));
	}
	
	static DatastorePb.Transaction localTxnToRemoteTxn(Transaction local) {
	    DatastorePb.Transaction remote = new DatastorePb.Transaction();
	    remote.setApp(local.getApp());
	    remote.setHandle(Long.parseLong(local.getId()));
	    return remote;
	  }


}
